import { CodeGenerator, CodeGeneratorInterface, Property, TagCodeGenerator } from "./CodeGenerator"
import { VueJsCodeEncoder } from "./JsCodeEncoder"
import { ApiMethod, OpenApi } from "./OpenApi"


export class TemplateCodeGenerator extends TagCodeGenerator {
  constructor(properties: Property[], children: TagCodeGenerator[]) {
    super('template', properties, false)
    this.children = children
    this.hasChild = children.length > 0
    this.childPrefix = '\n'
  }
}

export class ScriptCodeGenerator extends TagCodeGenerator {
  constructor(properties: Property[], children: CodeGeneratorInterface[]) {
    super('script', properties, false)
    this.children = children
    this.hasChild = children.length > 0
  }
}

export class StyleCodeGenerator extends TagCodeGenerator {
  constructor(properties: Property[], children: CodeGeneratorInterface[]) {
    super('style', properties, false)
    this.children = children
    this.hasChild = children.length > 0
  }
}

class JavaScriptCodeGenerator extends CodeGenerator {
  constructor(content: string) {
    super('JavaScriptCodeGenerator')
    if(content) {
      this.prefix = content + '\n'
    }
  }
}

class StyleSheetCodeGenerator extends CodeGenerator {
  constructor(content: string) {
    super('StyleSheetCodeGenerator')
    if(content) {
      this.prefix = content + '\n'
    }
  }
}
  

interface VueComponentOptions {
  tagName: string
  name?: string
  title?: string
  properties?: Property[]
  closeTag?: boolean
  tabCount?: number
  style?: string
}

export class VueComponent {
    _id: string
    vueComponentOptions: VueComponentOptions
    vueJsCodeEncoder: VueJsCodeEncoder
    children: VueComponent[]
  
    constructor(options: {
      vueComponentOptions?: VueComponentOptions
      vueJsCodeEncoder?: VueJsCodeEncoder
      children?: VueComponent[]
    }) {
      this.vueComponentOptions = Object.assign({
        tagName: '',
        name: '',
        properties: [],
        closeTag: false,
        tabCount: 0,
        style: ''
      }, options.vueComponentOptions)
      this.children = options.children || []
      this.vueJsCodeEncoder = options.vueJsCodeEncoder || new VueJsCodeEncoder()
      this.genId()
      
    }
    genId() {
      this._id = 'VueComponent_' + Date.now().toFixed(0) + '_'+ (Math.random() * 1000).toFixed(0).padStart(3, '0')
    }
  
    parseStyle(): CodeGeneratorInterface {
      var styleCodeGenerator = new StyleSheetCodeGenerator(this.vueComponentOptions.style)
      this.children.forEach(child => {
        styleCodeGenerator.addChild(child.parseStyle())
      })
      return styleCodeGenerator
    }
    parseScript(): CodeGeneratorInterface {
       var vueJsCodeEncoder = this.mergeEncoder()
      return new JavaScriptCodeGenerator(vueJsCodeEncoder.encode())
    }
    mergeEncoder() {
      var vueJsCodeEncoder = new VueJsCodeEncoder()
      vueJsCodeEncoder.merge(this.vueJsCodeEncoder)
      this.children.forEach((child) => {
        var childEncoder = child.mergeEncoder()
        vueJsCodeEncoder.merge(childEncoder)
      })
      return vueJsCodeEncoder
    }

    async parseTemplate(): Promise<CodeGeneratorInterface> {
      var tagCodeGenerator = new TagCodeGenerator(this.vueComponentOptions.tagName, this.vueComponentOptions.properties, this.vueComponentOptions.closeTag, this.vueComponentOptions.tabCount)
      await Promise.all(this.children.map(async child => {
        child.vueComponentOptions.tabCount = this.vueComponentOptions.tabCount + 1
        var childCodeGenerator = await child.parseTemplate()
        tagCodeGenerator.addChild(childCodeGenerator)
      }))
      await tagCodeGenerator.encode()
      return tagCodeGenerator
    }
  
    
    stringifyWithUndefined(obj: any) {
      return JSON.stringify(obj, function(_: string, value: any) {
        // 将 undefined 替换为 null
        if (value === undefined) {
          return '__undefined__';
        }
        return value;
      }, '\t').replace(/"__undefined__"/g, 'undefined');
    }
    
    async applyMethods(openapi: OpenApi, params: ApplyMethodsParamItem[]) : Promise<void> {
      await Promise.all(this.children.map((async child => {
          await child.applyMethods(openapi, params)
      })))
    }

    async encode(codeGenerator: SinglePageVueCodeGeneratorInterface) {
      if(codeGenerator instanceof VueCodeGenerator) {
        var template: TemplateCodeGenerator = new TemplateCodeGenerator([], [])
        var script: ScriptCodeGenerator = new ScriptCodeGenerator([], [])
        var style: StyleCodeGenerator = new StyleCodeGenerator([], [])
        codeGenerator.children = [template, script, style]
        codeGenerator.template = template
        codeGenerator.script = script
        codeGenerator.style = style
        codeGenerator.hasChild = true
        codeGenerator.childSuffix = '\n'
        template.addChild(await this.parseTemplate())
        script.addChild(this.parseScript())
        style.addChild(this.parseStyle())
      }
    }
  }
  
export class VueCodeGenerator extends CodeGenerator implements SinglePageVueCodeGeneratorInterface {
  component: SinglePageVueComponentInterface
  template?: TemplateCodeGenerator
  script?: ScriptCodeGenerator
  style?: StyleCodeGenerator
  title: string
  constructor(component: SinglePageVueComponentInterface) {
    super('VueCodeGenerator')
    this.component = component
    this.title = "VueCodeGenerator"
  }
  
  get pages() {
    return [{
      title: this.component.title,
      apiMethods: this.component.apiMethods,
      method: ''
    }]
  }
  
  async encode() {
    await this.component.encode(this)
  }

  updateComponent(component: SinglePageVueComponentInterface) {
    this.component = component
    this.encode()
  }

  async applyMethods(openapi: OpenApi, params: ApplyMethodsParamItem[]) {
    await this.component.applyMethods(openapi, params)
  }

  async genCode(): Promise<string> {
    await this.encode()
    return await super.genCode()
  }
}
  
export type ApplyMethodsParamItem = {
    name: string,
    title?: string
    controllerName?: string
    methodName?: string
    method?: string
    apiMethod?: ApiMethod
    controllersDir?: string
    definitionsDir?: string
    page?: string
}
  
export interface SinglePageVueComponentInterface {
  title: string
  apiMethods: ApplyMethodsParamItem[]
  applyMethods(openapi: OpenApi, params: ApplyMethodsParamItem[], ...args: any[]): Promise<void>
  encode(codeGenerator: SinglePageVueCodeGeneratorInterface): Promise<void>
}

export interface SinglePageVueCodeGeneratorInterface {
  title: string
  pages: SinglePageParam[]
  encode(): void
  updateComponent(component: SinglePageVueComponentInterface): void
  applyMethods(openapi: OpenApi, params: ApplyMethodsParamItem[]): Promise<void>
  genCode(): Promise<string>
}

export type SinglePageParam = {
  title: string
  apiMethods: ApplyMethodsParamItem[]
}